#!/usr/bin/env python3

import os
import sys
import re
import json5

#
# parse cmd line parameters, prior to conf file
#
def arg_parse(argv):
    args = {}
    for arg in argv:
        if arg.startswith('-'):
            arg = re.sub(r'^-+', '', arg)
            m = re.search(r'=', arg)
            if m:
                [k,v] = arg.split('=')
                args[k] = v
            else:
                args[arg] = True
    return args

args = arg_parse(sys.argv[1:])
print("ARGS: ", end="")
print(args)

#
# print help info and exit
#
if 'help' in args:
    print("""
1. Run simulation with Cadence Incisive
  python3 run_sim.py -tc=tc_sanity
  python3 run_sim.py -top=iic -tc=tc_sanity
2. Run Verdi
  python3 run_sim.py -verdi
  python3 run_sim.py -verdi -tc=tc_sanity
  python3 run_sim.py -verdi -top=iic -tc=tc_sanity
3. View RTL
  python3 run_sim.py -rtl_view
  python3 run_sim.py -rtl_view -top=iic
4. Switch Cadence Incisive to Synopsys VCS
  Edit conf.json, add "simulator"="vcs"
5. Print final command line info only for debug
  python3 run_sim.py ...  -dryrun
6. Specify or override fron conf file
  Edit conf.json, add "timescale=1ns/1ps", or "override_timescale=1ns/1ps"
""")
    sys.exit()
    
#
# get the root directory of current project by finding "root.txt"
#
def get_prj_root():
    prj_root = '.'
    for i in range(10):
        if os.path.exists(prj_root + '/root.txt'):
            return prj_root
        else:
            prj_root = prj_root + '/..'
    if i == 9:
        print("CAN NOT FIND PROJECT ROOT PATH")
        sys.exit()
        
prj_root = get_prj_root()
print("PRJ ROOT: " + prj_root)

#
# fetch conf file from cmd line, default is in "prj_root/sim/conf"
#
conf_file = "conf.json"
if 'conf' in args:
    conf_file = args['conf']
conf_full_file = "{}/sim/conf/{}".format(prj_root, conf_file)
if os.path.exists(conf_full_file):
    print("Use conf file: {}".format(conf_full_file))
    conf = json5.loads(open(conf_full_file, "r").read())
else:
    print("Conf file: {} NOT found".format(conf_full_file))
    sys.exit()

#
# get the tb top file
#
tb_top_file = conf['tb_top']
if 'top' in args:
    tb_top_file = args['top'] + '_tb.sv'
print("TB: " + tb_top_file)

#
# view only rtl, not includes testbench
#
if 'rtl_view' in args:
    if 'rtl_top' in conf:
        rtl_top_file = "{}/rtl/{}".format(prj_root, conf["rtl_top"])
    if 'top' in args:
        rtl_top_file = "{}/rtl/{}.v".format(prj_root, args["top"])
    print("RTL top file: " + rtl_top_file)

#
# other env files of testbench
#
env_files = ""
if 'env' in conf:
    for env_file in conf["env"]:
        env_file += " " + env_file

#
# -y and -v verilog library, or submodules
#
yvlib = ""
if 'ylib' in conf:
    for ylib in conf["ylib"]:
        yvlib += " -y {}/{}".format(prj_root, ylib)
if 'vlib' in conf:
    for vlib in conf["vlib"]:
        yvlib += " -v {}/{}".format(prj_root, vlib)

#
# specify current test case
#
tc = 'tc_sanity'
if 'tc' in args:
    tc = args['tc']
os.system("/usr/bin/rm -rf testcase.sv")
os.symlink("{}/sim/tc/{}.sv".format(prj_root, tc), "testcase.sv")
print("TC: {}.sv".format(tc))

#
# Run Verdi to Debug, or view waveform, or view rtl only
#
if 'verdi' in args:
    cmd = "verdi -nologo -sv"
    cmd += " +define+SIM"
    cmd += " -ssy -ssv"
    cmd += " +libext+.v+.sv"
    cmd += yvlib
    if 'incdir' in conf:
        for incdir in conf["incdir"]:
            cmd += " +incdir+{}".format(incdir)
    if env_files != "":
        cmd += env_files
    if 'rtl_view' in args:
        cmd += " " + rtl_top_file
    else:
        cmd += " " + prj_root + "/sim/tb/" + tb_top_file
    if 'libs' in conf:
        for lib in conf['libs']:
            cmd += " " + prj_root + "/" + lib
else:
    #
    # Run simulator
    #
    sim_define_type = "-"
    if 'simulator' in conf:
        if conf['simulator'] == "vcs":
            sim_define_type = "+"

    # set default simulator
    if not 'simulator' in conf:
        conf['simulator'] = "irun"

    if conf['simulator'] == "vcs":
        cmd = "vcs -full64 -sverilog"
        cmd += " -debug_all"

        # override timescale from conf file
        if 'override_timescale' in conf:
            cmd += " -override_timescale=" + conf['override_timescale']
        # specify default timescale from conf file
        elif 'timescale' in conf:
            cmd += " -timescale=" + conf['timescale']
        # use 1ns/1ps in post simulation
        elif 'post' in args:
            cmd += " -timescale=1ns/1ps"
        # use 1ns/1ns in functional sim to speed 
        else:
            cmd += " -timescale=1ns/1ns"
            
        cmd += " +define+SIM"
        
        if 'incdir' in conf["incdir"]:
            for incdir in conf["incdir"]:
                cmd += " +incdir+{}".format(incdir)
        cmd += " +incdir+.";
        
        cmd += " +libext+.v+.sv"
        
    elif conf['simulator'] == "xrun" or conf['simulator'] == "irun":
        if conf['simulator'] == "xrun":
            cmd = "xrun"
        else:
            cmd = "irun"
        cmd += " -64bit -access rwc"
        
        if 'override_timescale' in conf:
            cmd += " -override_timescale"
            cmd += " -timescale " + conf['override_timescale']
        elif 'timescale' in conf:
            cmd += " -timescale " + conf['timescale']
        elif 'post' in args:
            cmd += " -timescale 1ns/1ps"
        else:
            cmd += " -timescale 1ns/1ns"
            
        cmd += " -define SIM"
        
        if 'incdir' in conf["incdir"]:
            for incdir in conf["incdir"]:
                cmd += " -incdir {}".format(incdir)
        cmd += " -incdir .";
        
        cmd += " -libext .v";
        cmd += " -libext .sv"

    # set log file
    cmd += " -l sim.log"

    # yvlib
    cmd += yvlib

    # sv or uvm packages
    if env_files != "":
        cmd += env_files

    # simulation top file
    if 'rtl_view' in args:
        cmd += " " + rtl_top_file
    else:
        cmd += " " + prj_root + "/sim/tb/" + tb_top_file

    # other libs
    if 'libs' in conf:
        for lib in conf['libs']:
            cmd += " " + prj_root + "/" + lib

    # other options
    if 'other_options' in conf:
        for other_options in conf['other_options']:
            cmd += " " + other_options
            
#
# Run final command
#
if 'dryrun' in args:
    print(cmd)
else:
    print(cmd)
    os.system(cmd)


